program pavio
  use param
  use run_param,only:n_temp_profile
  use run_param,only:temp_profile
  implicit none

#ifdef MPI
  include 'mpif.h'
#endif
  
  ! argument
  integer :: nargc

  ! error message
  character(len=100) :: err_msg

  ! mpi parameters
  integer :: nprocs,myrank,ierr,dest,tag,r
  character(len=5) :: rankstr
  character(len=100) :: msg

  ! default io files
  character(len=len_fn) :: ipfn,lgfn,opfn,chfn
  integer :: iunit,ounit,lunit,cunit

  ! system time
  real :: run_time,sys_etime,sys_time(2)
  integer :: time_array_0(8),time_array_1(8)
  character(len=10) :: d_time_str
  character(len=50) :: c_time_str

  ! Terminal parameters
  integer :: twidth
  character(len=len_linebuf) :: tstr

  ! run parameters
  real(dbl) :: total_dpa,total_sdpa,total_time,total_loop

  ! Dummies
  integer :: i
  real(dbl) :: zero

  ! code version
  character(len=3) :: str_ver
  real :: code_ver

#ifdef MPI
  integer :: status(MPI_STATUS_SIZE)
#endif

  code_ver=0.3
  write(str_ver,'(f3.1)') code_ver

#ifdef MPI
  call MPI_INIT(ierr)
  call MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
  call MPI_COMM_SIZE(MPI_COMM_WORLD, nprocs, ierr)
#else
  nprocs=1
  myrank=0
#endif

  zero=0.0

  ! unit of the default io files
  iunit=10
  ounit=11
  lunit=12
  cunit=13

  ! Terminal width
  twidth=56

  ! Record the system time
  call date_and_time(values=time_array_0)

  ! Treat input arguments, basically 3 io-files 
  ! require to run the code:input file, output file, log file
  nargc=iargc()
  if (nargc.eq.0) then
     call abnormal_stop("main","Insufficient input argument.")
  else if (nargc.eq.1) then
     call getarg(1,ipfn)
     opfn=trim(ipfn)//".out"
     lgfn=trim(ipfn)//".log"
  else if (nargc.eq.2) then
     call getarg(1,ipfn)
     call getarg(2,opfn)
     lgfn=trim(ipfn)//".log"
  else if (nargc.ge.3) then
     call getarg(1,ipfn)
     call getarg(2,opfn)
     call getarg(3,lgfn)
  end if

#ifdef DEBUG
  ! if debugging mode turns on,
  chfn=trim(ipfn)//".chk"
#endif

  ! Conversion inherent rank number to string
  write(rankstr,'(i0)') myrank
  opfn=trim(opfn)//".r"//trim(rankstr)
  lgfn=trim(lgfn)//".r"//trim(rankstr)

#ifdef DEBUG
  ! if debugging mode turns on,
  chfn=trim(chfn)//".r"//trim(rankstr)
#endif

  ! Open the io-files
  open(unit=iunit,file=ipfn,status='old')
  open(unit=lunit,file=lgfn,status='replace')
  open(unit=ounit,file=opfn,status='replace')

#ifdef DEBUG
  ! if debugging mode turns on,
  open(unit=cunit,file=chfn,status='replace')
#endif

  ! Record the current time
  call print_c_time(time_array_0,c_time_str)

  if (myrank.eq.0) call print_head(str_ver,c_time_str,nprocs)
  call write_head(lunit,str_ver,c_time_str,myrank,nprocs)
  call write_file_info(lunit,ipfn,opfn,chfn)

  ! Initialize the variables before reading the input script
  call init_global_param
  call init_output_param
  call init_data_param
  call init_mat_param
  call init_box_param
  call init_group_param
  call init_motion_param
  call init_edit_param
  call init_table_param
  call init_run_param

  ! Read the input file
  call readinp(iunit,lunit,myrank)
  close(iunit)
  if (myrank.eq.0) then
     call print_d_time(time_array_0,d_time_str)
     call terminal_msg1("")
     print '(a8,":READINP DONE.")',d_time_str(1:8)
  ! Verify the input script.
  end if
#ifdef DEBUG
  if (myrank.eq.0) call check_readinp(cunit)
#endif

  ! readlabel
  call readlabel(lunit,time_array_0,myrank)
  call init_object_type(lunit)
#ifdef DEBUG
  call check_readlabel(cunit)
#endif

  ! readdiff
  call readdiff(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readdiff(cunit)
#endif

  ! readdiss
  call readdiss(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readdiss(cunit)
#endif

  ! readrctn
  call init_react_data(lunit)
  call readrctn(lunit,time_array_0,myrank)
#ifdef DEBUG
  if (myrank.eq.0) call check_readrctn(cunit)
#endif

  ! readextn
  call readextn(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readextn(cunit)
#endif

  ! readgeom
  call init_geom_data(lunit)
  call readgeom(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readgeom(cunit)
#endif

  ! readsink
  call init_sink_data(lunit)
  call readsink(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readsink(cunit)
#endif

  ! readsource
  call readsrce(lunit,time_array_0,myrank)
#ifdef DEBUG
  call check_readsrce(cunit)
#endif

  ! Determine the unit of the system
  call set_system_unit

  ! Initialize the react group list for all
  call init_react_group(lunit)
  call check_react_group(lunit)
#ifdef DEBUG
  call check_react_list(cunit)
#endif

  ! Determine the channel for spatial distribution
  call set_channel(lunit)

  ! Set the virtual box for fast calculation 
  ! of reaction between objects
  call set_vbox(lunit,time_array_0,myrank)

  ! Set material parameters
  call set_mat_param(lunit)

  ! Initialize the random seed
  call init_random_seed(lunit,nprocs,myrank)

  ! Set the external event
  call set_external_event(lunit)

  ! Initialize the table
  call init_table(lunit)

  ! Initialize the maping function for profile tables
  call init_profile_map(lunit)

  ! Initialize the recording devices
  call init_count_param(lunit,myrank)
  call check_count_param(lunit)

  total_dpa=0
  total_sdpa=0
  total_time=0
  total_loop=0

  call print_start_msg(lunit,cunit,time_array_0,&
       n_temp_profile,myrank)

  ! Main loop
  do i=1,n_temp_profile

     ! Determine the rate constant
     call set_transition_rate(lunit,temp_profile(i)%temp)

! #ifdef DEBUG
!      call check_transition_rate(cunit,i,temp_profile(i)%temp)
! #endif

     ! Update the profile table
     call update_event_profile(lunit)

     ! Add the events associated with the source
     if (i.eq.1) call add_source_event(lunit)

     ! Add the external event
     call add_external_event(lunit,&
          temp_profile(i)%external_event_flag)

     ! Read the initial condition at the first loop
     if (i.eq.1) call read_initial_condition(lunit)

     ! Print the run messages
     if (i.eq.1) call print_init_table(lunit,cunit)
     if (i.eq.1) call print_count_obj_header(lunit,ounit,myrank)
     call print_profile_msg(lunit,time_array_0,i,myrank)

     ! stop_condition depends on loop_condition
     select case(temp_profile(i)%loop_condition)
     case(1)
        ! time
        call time_loop(lunit,cunit,ounit,&
             temp_profile(i)%stop_condition,&
             total_time,total_dpa,total_sdpa,total_loop,&
             temp_profile(i)%temp,myrank)
     case(2)
        ! loop
        call loop_loop(lunit,cunit,ounit,&
             temp_profile(i)%stop_condition,&
             total_time,total_dpa,total_sdpa,total_loop,&
             temp_profile(i)%temp,myrank)
     case(3)
        ! dpa
        call dpa_loop(lunit,cunit,ounit,&
             temp_profile(i)%stop_condition,&
             total_time,total_dpa,total_sdpa,total_loop,&
             temp_profile(i)%temp,myrank)
     end select
  end do

#ifdef MPI
  if (myrank.ne.0) then
     dest=0
     tag=0
     msg='Rank'//trim(rankstr)//'-Job is done..'
     ! Finalize the run
     call MPI_Send(msg,len_trim(msg),MPI_CHARACTER,&
          dest,tag,MPI_COMM_WORLD,ierr)
  else
     tag=0
     do r=1,nprocs-1
        call MPI_Recv(msg,100,MPI_CHARACTER,r,tag,&
             MPI_COMM_WORLD,status,ierr)
        print '(1x,a)',trim(msg)
     end do
     print '(1x,"Root(",i0,")-Job is done.")',myrank
     call print_exit_msg(lunit,cunit,twidth,&
          total_time,total_dpa,total_sdpa,total_loop,&
          time_array_0,myrank)
  end if
  call MPI_FINALIZE(ierr)
#endif

  ! Free the allocated memories
  call freemem

  ! Close the io files
#ifdef DEBUG
  close(cunit)
#endif

  close(ounit)
  close(lunit)

end program pavio
